﻿using System;

[assembly: CLSCompliant(false)]
namespace Microsoft.ExternalResearch.CreativeCommons.Common
{
    using System.Drawing;
    using System.Globalization;
    using System.IO;
    using System.Net;
    using System.Text;
    using System.Windows.Forms;
    using System.Xml;
    using Microsoft.Office.Core;

    /// <summary>
    /// Helper class for Creative Commons Microsoft Office add-in.
    /// </summary>
    public static class Helper
    {
        /// <summary>
        /// Number of minutes the disk cahche needs to be maintained for.
        /// </summary>
        public const int CacheMinutes = 60;

        /// <summary>
        /// Name of the license image file for each license cached on disk.
        /// </summary>
        public const string LicenseImageFileName = "license.img";

        /// <summary>
        /// Name of the license XML file for each license cached on disk.
        /// </summary>
        public const string LicenseFileName = "license.xml";

        /// <summary>
        /// Name of the property that contains the ID of license.
        /// </summary>
        public const string LicenseIdPropertyName = "CreativeCommonsLicenseID";

        /// <summary>
        /// Name of the property that contains the URL of license.
        /// </summary>
        public const string LicenseUrlPropertyName = "CreativeCommonsLicenseURL";

        /// <summary>
        /// Name of the property that contains the license XML.
        /// </summary>
        public const string LicenseXmlPropertyName = "CreativeCommonsLicenseXml";

        /// <summary>
        /// Name of the folder where licenses will be chached.
        /// </summary>
        public const string LicenseFolderName = "Creative Commons Licenses";

        /// <summary>
        /// Gets the image size for license images.
        /// </summary>

        private const int ImageWidth = 88;
        private const int ImageHeight = 31;
        public static Size ImageSize
        {
            get
            {
                return new Size(ImageWidth, ImageHeight);
            }
        }

        private const float pixelPointRatio = 0.72F;
        public static float PixelPointRatio
        {
            get
            {
                return pixelPointRatio;
            }
        }

        /// <summary>
        /// Gets the log filename.
        /// </summary>

        public static string CCAddInLog
        {
            get
            {
                return "CC-AddIn*.log";
            }
        }

        /// <summary>
        /// Gets the path for the file containing information about license types.
        /// </summary>
        public static string LicenseTypesFilePath
        {
            get
            {
                return Path.Combine(Helper.LocalLicensesFolderPath, "licensetypes.xml");
            }
        }

        /// <summary>
        /// Gets the path where information about licenses is stored.
        /// </summary>
        public static string LocalLicensesFolderPath
        {
            get
            {
                return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), LicenseFolderName);
            }
        }


        /// <summary>
        /// Returns the localized string.
        /// </summary>
        /// <param name="id">ID of the string resource.</param>
        /// <returns>Value of the string resource.</returns>
        public static string GetString(string id)
        {
            return Properties.Resources.ResourceManager.GetString(id);
        }

        /// <summary>
        /// Returns the localized string formatted with the specified parameters.
        /// </summary>
        /// <param name="id">ID of the resource string.</param>
        /// <param name="args">Parameters needs to be used for formatting.</param>
        /// <returns>Resource string formatted with specified parameters.</returns>
        public static string GetString(string id, object[] args)
        {
            string res = Properties.Resources.ResourceManager.GetString(id);
            if (null != args && args.Length > 0)
            {
                return String.Format(CultureInfo.InvariantCulture, res, args);
            }

            return res;
        }

        /// <summary>
        /// Returns the license information that can be displayed in the ribbon UI.
        /// </summary>
        /// <param name="licenseFolder">License folder.</param>
        /// <returns>String containing the information that can be displayed in the ribbon UI.</returns>
        public static string GetLicenseLabel(string licenseFolder)
        {
            if (null == licenseFolder)
            {
                return GetString("NoInformationAvailable");
            }
            string[] licenseInfoArray = Helper.GetLicenseInfoForDisplay(licenseFolder);
            string licenseLabel = String.Empty;

            if (null != licenseInfoArray[0])
            {
                licenseLabel += Helper.GetString("Jurisdiction") + Helper.GetString("EqualSymbol") + licenseInfoArray[0];
            }

            if (string.IsNullOrEmpty(licenseInfoArray[0]) && licenseFolder.Equals("publicdomain"))
            {
                licenseLabel = Helper.GetString("PublicDomain");
            }

            if (string.IsNullOrEmpty(licenseInfoArray[0]) && licenseFolder.Equals("mark"))
            {
                licenseLabel = Helper.GetString("PublicDomainMark");
            }

            if (!string.IsNullOrEmpty(licenseInfoArray[1]))
            {
                licenseLabel += Helper.GetString("CommaSpace") + Helper.GetString("Commercial") + Helper.GetString("EqualSymbol") + licenseInfoArray[1];
            }

            if (!string.IsNullOrEmpty(licenseInfoArray[2]))
            {
                licenseLabel += Helper.GetString("CommaSpace") + Helper.GetString("Derivatives") + Helper.GetString("EqualSymbol") + licenseInfoArray[2];
            }

            if (!string.IsNullOrEmpty(licenseInfoArray[3]))
            {
                licenseLabel += Helper.GetString("CommaSpace") + Helper.GetString("Sampling") + Helper.GetString("EqualSymbol") + licenseInfoArray[3];
            }

            return String.IsNullOrEmpty(licenseLabel) ? Helper.GetString("NoInformationAvailable") : licenseLabel;
        }

        /// <summary>
        /// Reads the license from the document.
        /// </summary>
        /// <param name="properties">Custom properties collection.</param>
        /// <returns>A LicenseInfo object or null.</returns>
        public static LicenseInfo ReadLicenseInfo(DocumentProperties properties)
        {
            LicenseInfo licenseInfo = null;
            if (null == properties || 0 == properties.Count)
            {
                return licenseInfo;
            }
            foreach (DocumentProperty prop in properties)
            {
                if (prop.Name == Helper.LicenseIdPropertyName)
                {
                    if (null == licenseInfo)
                    {
                        licenseInfo = new LicenseInfo();
                        licenseInfo.Id = prop.Value.ToString();
                        licenseInfo.LicenseUrl = properties[Helper.LicenseUrlPropertyName].Value.ToString();
                        licenseInfo.LicenseXml = properties[Helper.LicenseXmlPropertyName].Value.ToString();
                        break;
                    }
                }
            }

            return licenseInfo;
        }

        /// <summary>
        /// Saves the license information to the document.
        /// </summary>
        /// <param name="properties">Custom properties collection.</param>
        /// <param name="licenseInfo">LicenseInfo object containing license information.</param>
        public static void SaveLicenseInfo(DocumentProperties properties, LicenseInfo licenseInfo)
        {
            //Validate the arguments and return in case of error
            if (null == properties || null == licenseInfo)
            {
                return;
            }

            if (null != Helper.GetDocumentProperty(properties, Helper.LicenseIdPropertyName))
            {
                properties[Helper.LicenseIdPropertyName].Delete();
                properties[Helper.LicenseUrlPropertyName].Delete();
                properties[Helper.LicenseXmlPropertyName].Delete();
            }

            properties.Add(Helper.LicenseIdPropertyName, false, MsoDocProperties.msoPropertyTypeString, licenseInfo.Id, Type.Missing);
            properties.Add(Helper.LicenseUrlPropertyName, false, MsoDocProperties.msoPropertyTypeString, licenseInfo.LicenseUrl, Type.Missing);
            properties.Add(Helper.LicenseXmlPropertyName, false, MsoDocProperties.msoPropertyTypeString, licenseInfo.LicenseXml, Type.Missing);
        }

        /// <summary>
        /// Creates the disk cache folders.
        /// </summary>
        public static void PrepareLicenseFolders()
        {
            if (!Directory.Exists(LocalLicensesFolderPath))
            {
                Directory.CreateDirectory(LocalLicensesFolderPath);
            }
        }

        /// <summary>
        /// Return the image from the path specified.
        /// </summary>
        /// <param name="imageFilePath">Path of the image file.</param>
        /// <returns>Image object.</returns>
        public static Image GetImageFromFile(string imageFilePath)
        {
            Image image = null;
            // Bitmap.FromFile() locks the opened file. We don't want to lock the image files.
            if (File.Exists(imageFilePath))
            {
                using (Stream stream = File.Open(imageFilePath, FileMode.Open))
                {
                    image = Image.FromStream(stream);
                }
            }

            return image;
        }

        /// <summary>
        /// Displays a question message box with Yes/No buttons and returns the response.
        /// </summary>
        /// <param name="message">Message to be displayed.</param>
        /// <returns>True if user selcts Yes, false otherwise.</returns>
        public static bool GetUserResponse(string message)
        {
            if (DialogResult.No == MessageBox.Show(message, Helper.GetString("Question"), MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1, (MessageBoxOptions)0))
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Displays an error message box.
        /// </summary>
        /// <param name="message">Error message to be displayed.</param>
        public static void DisplayErrorMessage(string message)
        {
            MessageBox.Show(
                message,
                GetString("Error"),
                MessageBoxButtons.OK,
                MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, (MessageBoxOptions)0);
        }

        /// <summary>
        /// Returns the value of the specified property from the specified custom properties.
        /// Returns null if the property does not exists in the custom properties.
        /// </summary>
        /// <param name="properties">Custom properties collection.</param>
        /// <param name="propertyName">Name of the property to be looked for.</param>
        /// <returns>Value of document property or null.</returns>
        public static string GetDocumentProperty(DocumentProperties properties, string propertyName)
        {
            if (null == properties || null == propertyName)
            {
                return null;
            }

            if (0 == properties.Count)
            {
                return null;
            }

            foreach (DocumentProperty prop in properties)
            {
                if (prop.Name == propertyName)
                {
                    return prop.Value.ToString();
                }
            }

            return null;
        }

        /// <summary>
        /// Returns the HTML information for the specified license.
        /// </summary>
        /// <param name="licensePath">Path of the license.</param>
        /// <returns>Array containing license information.</returns>
        public static string[] GetLicenseInfo(string licensePath)
        {
            string[] licenseInfo = new string[5];
            XmlDocument xdoc = new XmlDocument();

            xdoc.Load(Helper.LocalLicensesFolderPath + @"\" + licensePath + @"\" + Helper.LicenseFileName);
            // Link Text
            // Link Url
            // Pre Text
            // Post Text
            // XML
            if (xdoc.SelectNodes("result/html/a").Count > 0)
            {
                licenseInfo[0] = xdoc.SelectNodes("result/html/a")[1].InnerText;
                licenseInfo[1] = xdoc.SelectNodes("result/html/a")[1].Attributes["href"].Value;
            }
            else
            {
                licenseInfo[0] = xdoc.SelectNodes("result/html/p")[0].InnerText;
                licenseInfo[1] = xdoc.SelectNodes("result/html/p/a")[0].Attributes["href"].Value;
            }
            licenseInfo[2] = xdoc.SelectNodes("result/html")[0].InnerText.Substring(0, xdoc.SelectNodes("result/html")[0].InnerText.IndexOf(licenseInfo[0], StringComparison.CurrentCulture));
            licenseInfo[3] = xdoc.SelectNodes("result/html")[0].InnerText.Substring(licenseInfo[2].Length + licenseInfo[0].Length);

            int length = xdoc.SelectNodes("result/html")[0].InnerText.Length - licenseInfo[0].Length;
            // Save entire license XMl into the element.
            licenseInfo[4] = xdoc.InnerXml;

            return licenseInfo;
        }

        /// <summary>
        /// Returns the license information. This information is used by the Office ribbon to display the 
        /// available licenses.
        /// </summary>
        /// <param name="licensePath">Path of the license.</param>
        /// <returns>Array containing license information to be displayed in Office ribbon.</returns>
        public static string[] GetLicenseInfoForDisplay(string licensePath)
        {
            // 0-Jurisdication
            // 1-Commercial
            // 2-Derivatives
            // 3-Sampling
            string[] info = new string[4];
            //Return null array
            if (string.IsNullOrEmpty(licensePath))
            {
                return null;
            }

            string[] licenseInfoArray = licensePath.Split('&');
            foreach (string s in licenseInfoArray)
            {
                if (s.StartsWith("jurisdiction", StringComparison.CurrentCulture))
                {
                    info[0] = s.Split('=')[1];

                    if (string.IsNullOrEmpty(info[0]))
                    {
                        info[0] = Helper.GetString("Generic");
                    }
                }

                if (s.StartsWith("commercial", StringComparison.CurrentCulture))
                {
                    info[1] = s.Split('=')[1];
                }

                if (s.StartsWith("derivatives", StringComparison.CurrentCulture))
                {
                    info[2] = s.Split('=')[1];
                }

                if (s.StartsWith("sampling", StringComparison.CurrentCulture))
                {
                    info[3] = s.Split('=')[1];
                }
            }

            return info;
        }

        /// <summary>
        /// Checks if the conenction to specified server is available.
        /// </summary>
        /// <param name="strServer">URL of the server.</param>
        /// <returns>true if connection is available, false otherwise.</returns>
        public static bool ConnectionAvailable(string strServer)
        {
            try
            {
                HttpWebRequest reqFP = (HttpWebRequest)HttpWebRequest.Create(strServer);
                HttpWebResponse rspFP = (HttpWebResponse)reqFP.GetResponse();

                if (HttpStatusCode.OK == rspFP.StatusCode)
                {
                    // HTTP = 200 - Internet connection available, server online
                    rspFP.Close();
                    return true;
                }
                else
                {
                    // Other status - Server or connection not available
                    rspFP.Close();
                    return false;
                }
            }
            catch (WebException)
            {
                // Exception - connection not available
                return false;
            }
            catch (XmlException)
            {
                // Exception - connection not available
                return false;
            }
            catch (AccessViolationException)
            {
                // Exception - connection not available
                return false;
            }
        }

        #region region Image to Xml conversion (for license image)

        public static string ConvertImageToXml(Image img)
        {
            MemoryStream stream = new MemoryStream();
            XmlTextWriter writer = null;
            try
            {
                writer = new XmlTextWriter(stream, Encoding.ASCII);
                WriteWordDocFregment(writer, img);
            }
            finally
            {
                writer.Close();
                stream.Close();
            }

            return ASCIIEncoding.ASCII.GetString(stream.ToArray());
        }

        /// <summary>
        /// Write the whole WordML document
        /// </summary>
        /// <param name="wtr">The XmlTextWriter to write to</param>
        private static void WriteWordDocFregment(XmlTextWriter writer, Image img)
        {
            string WordMLNS = "http://schemas.microsoft.com/office/word/2003/wordml";
            string VMLNS = "urn:schemas-microsoft-com:vml";
            string OfficeNS = "urn:schemas-microsoft-com:office:office";

            byte[] data;
            string title = string.Empty;
            string name = string.Empty;

            ////////////////////////////////////////////////////
            // Need to grab the raw, compressed data - do this by saving the image to a MemoryStream
            MemoryStream stream = new MemoryStream();
            img.Save(stream, img.RawFormat);
            data = stream.ToArray();
            stream.Close();

            // Construct the WordML document fragment wrapper.  
            // WordML fragment have to be complete WordML documents including full namespace referenced.

            // start <xml> tag
            writer.WriteStartDocument();

            // add processing instructions
            writer.WriteProcessingInstruction("mso-application", "progid=\"Word.Document\"");

            // start <wordDocument> tag  
            writer.WriteStartElement("w", "wordDocument", WordMLNS);

            // write namespaces  
            writer.WriteAttributeString("xmlns", "w", null, WordMLNS);
            writer.WriteAttributeString("xmlns", "v", null, VMLNS);
            writer.WriteAttributeString("xmlns", "o", null, OfficeNS);

            // start <body> tag  
            writer.WriteStartElement("body", WordMLNS);

            // call WritePict to add our image to the Xml stream.
            ///////////////////////////////////////////////
            // start <pict> tag
            writer.WriteStartElement("pict", WordMLNS);

            // start <binData> tag
            writer.WriteStartElement("binData", WordMLNS);

            writer.WriteAttributeString("name", WordMLNS, string.Format(CultureInfo.CurrentCulture, "wordml://{0}{1}", name, string.Empty));

            // write the image as Base64
            writer.WriteBase64(data, 0, data.Length);

            // end <binData> tag
            writer.WriteEndElement();

            // start <shape> tag which describes the shape containing the image
            writer.WriteStartElement("shape", VMLNS);
            writer.WriteAttributeString("id", "_x0000_" + name);
            writer.WriteAttributeString("style", string.Format(CultureInfo.CurrentCulture, "width:{0}px;height:{1}px", img.Size.Width, img.Size.Height));

            // start <imagedata> tag which links to the <binData> above.
            writer.WriteStartElement("imagedata", VMLNS);
            writer.WriteAttributeString("src", string.Format(CultureInfo.CurrentCulture, "wordml://{0}{1}", name, string.Empty));
            writer.WriteAttributeString("title", OfficeNS, title);

            // end <imagedata> tag
            writer.WriteEndElement();

            // end <shape> tag
            writer.WriteEndElement();

            // end <pict> tag
            writer.WriteEndElement();
            ///////////////////////////////////////////////

            // end <body> tag  
            writer.WriteEndElement();

            // end <wordDocument> tag  
            writer.WriteEndElement();

            // end <xml> tag
            writer.WriteEndDocument();
        }

        #endregion

    }
}
